package com.webank.webase.front.gm.pressure;

import com.google.common.util.concurrent.RateLimiter;
import com.webank.webase.front.gm.basic.HelloWorldGM;
import org.fisco.bcos.channel.client.Service;
import org.fisco.bcos.web3j.crypto.Credentials;
import org.fisco.bcos.web3j.crypto.gm.GenCredential;
import org.fisco.bcos.web3j.protocol.Web3j;
import org.fisco.bcos.web3j.protocol.channel.ChannelEthereumService;
import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
import org.fisco.bcos.web3j.utils.Web3AsyncThreadPoolSize;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.math.BigInteger;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.Assert.assertTrue;

public class PerformanceHelloWorld {
  public static Logger logger = LoggerFactory.getLogger(PerformanceHelloWorld.class);
  private static AtomicInteger sended = new AtomicInteger(0);

  public static void main(String[] args) throws Exception {
    try {
      String groupId = args[3];
      ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
      Service service = context.getBean(Service.class);
      service.setGroupId(Integer.parseInt(groupId));
      service.run();

      System.out.println("Start test...");
      System.out.println("===================================================================");

      ChannelEthereumService channelEthereumService = new ChannelEthereumService();
      channelEthereumService.setChannelService(service);

      Web3AsyncThreadPoolSize.web3AsyncCorePoolSize = 3000;
      Web3AsyncThreadPoolSize.web3AsyncPoolSize = 2000;

      ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(500);
      Web3j web3 = Web3j.build(channelEthereumService, 15 * 100, scheduledExecutorService, Integer.parseInt(groupId));

      Credentials credentials = Credentials.create("b83261efa42895c38c6c2364ca878f43e77f3cddbc922bf57d0d48070f79feb6");

      BigInteger gasPrice = new BigInteger("30000000");
      BigInteger gasLimit = new BigInteger("30000000");

      String command = args[0];
      Integer count = 0;
      Integer qps = 0;

      switch (command) {
      case "trans":
        count = Integer.parseInt(args[1]);
        qps = Integer.parseInt(args[2]);
       // break;
      default:
        System.out.println("Args: <trans> <Total> <QPS>");
      }

      ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
      threadPool.setCorePoolSize(200);
      threadPool.setMaxPoolSize(500);
      threadPool.setQueueCapacity(count);

      threadPool.initialize();

      System.out.println("Deploying contract...");
      HelloWorldGM helloWorldGM = HelloWorldGM.deploy(web3, credentials, gasPrice, gasLimit).send();

      PerformanceCollector collector = new PerformanceCollector();
      collector.setTotal(count);

      RateLimiter limiter = RateLimiter.create(qps);
      Integer area = count / 10;
      final Integer total = count;

      System.out.println("Start test，total：" + count);
      for (Integer i = 0; i < count; ++i) {
        threadPool.execute(new Runnable() {
          @Override
          public void run() {
            limiter.acquire();
            PerformanceHelloWorldCallback callback = new PerformanceHelloWorldCallback();
            callback.setCollector(collector);
            try {
              helloWorldGM.set("4", callback);
            } catch (Exception e) {
              TransactionReceipt receipt = new TransactionReceipt();
              receipt.setStatus("-1");

              callback.onResponse(receipt);
              logger.info(e.getMessage());
            }

            int current = sended.incrementAndGet();

            if (current >= area && ((current % area) == 0)) {
              System.out.println("Already sended: " + current + "/" + total + " transactions");
            }
          }
        });
      }
    } catch (Exception e) {
      e.printStackTrace();
//      System.exit(-1);
    }
  }


  @Test
  public  void main() throws InterruptedException {
    System.out.println("&&&&&&&&&&&&&&&&&&");
    try {
      String groupId = "1";
      ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
      Service service = context.getBean(Service.class);
      service.setGroupId(Integer.parseInt(groupId));
      service.run();

      System.out.println("Start test...");
      System.out.println("===================================================================");

      ChannelEthereumService channelEthereumService = new ChannelEthereumService();
      channelEthereumService.setChannelService(service);

      Web3AsyncThreadPoolSize.web3AsyncCorePoolSize = 3000;
      Web3AsyncThreadPoolSize.web3AsyncPoolSize = 2000;

      ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(500);
      Web3j web3 = Web3j.build(channelEthereumService, 15 * 100, scheduledExecutorService, Integer.parseInt(groupId));

      Credentials credentials = GenCredential.create("b83261efa42895c38c6c2364ca878f43e77f3cddbc922bf57d0d48070f79feb6");

      BigInteger gasPrice = new BigInteger("3");
      BigInteger gasLimit = new BigInteger("30000000");

      Integer count = 0;
      Integer qps = 0;

        count = Integer.parseInt("10000");
        qps = Integer.parseInt("1000");


      ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
      threadPool.setCorePoolSize(2000);
      threadPool.setMaxPoolSize(2000);
      threadPool.setQueueCapacity(count);

      threadPool.initialize();

      System.out.println("Deploying contract...");
      HelloWorldGM helloWorldGM = HelloWorldGM.deploy(web3, credentials, gasPrice, gasLimit).send();

      PerformanceCollector collector = new PerformanceCollector();
      collector.setTotal(count);

      RateLimiter limiter = RateLimiter.create(qps);
      Integer area = count / 10;
      final Integer total = count;

      System.out.println("Start test，total：" + count);
      for (Integer i = 0; i < count; ++i) {
        threadPool.execute(new Runnable() {
          @Override
          public void run() {
            limiter.acquire();
            PerformanceHelloWorldCallback callback = new PerformanceHelloWorldCallback();
            callback.setCollector(collector);
            try {
                int i = new Random().nextInt(1000000);
              helloWorldGM.set("4"+ i , callback);
            } catch (Exception e) {
              TransactionReceipt receipt = new TransactionReceipt();
              receipt.setStatus("-1");

              callback.onResponse(receipt);
              logger.info(e.getMessage());
            }

            int current = sended.incrementAndGet();

            if (current >= area && ((current % area) == 0)) {
              System.out.println("Already sended: " + current + "/" + total + " transactions");
            }
          }
        });
      }
      assertTrue(count>0);
    } catch (Exception e) {
      e.printStackTrace();
    //  System.exit(-1);
    }

    Thread.sleep(100000);

  }




}
